#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jul  8 16:42:56 2018
@project: 天池比赛-A股主板上市公司公告信息抽取
@group: MZH_314
@author: LHQ


公告表格的数值转换
"""
import re
import decimal
from decimal import Decimal


class BaseConverter:
    """
    转换器抽象类
    """
    def __call__(self, s):
        if not isinstance(s, str):
            return s
        else:
            return self.convert(s)
        
    def convert(self, s: str) -> str:
        """转换动作
        
        Args:
        ----
        s: 待转换的字符串

        Returns
        -------
        返回转换后的字符串
        
        Raises
        ------
        NotImplementedError: 没有实现'convert'方法"
        """
        raise NotImplementedError("未实现'convert'方法")


class DateConverter(BaseConverter):
    """日期转换器

    将公告表格里的日期转换成统一的日期格式
    针对区间性日期，则提取最后一个日期
    """
    def __init__(self):
        self._patterns = []
        
    def add_pattern(self, regexp):
        """添加正则表达式字符串
        """
        p = re.compile(regexp)
        self._patterns.append(p)

    def _extract(self, matchobj):
        gd = matchobj.groupdict()
        tmp = []
        y = gd.get('y')
        m = gd.get('m')
        d = gd.get('d')
        if y is not None:
            tmp.append(y)
        if m is not None:
            if len(m) == 1:
                m = '0' + m
            tmp.append(m)
        if d is not None:
            if len(d) == 1:
                d = '0' + d
            tmp.append(d)
        date = "-".join(tmp)
        return date

    def process(self, s: str):
        """字符串预处理
        """
        s = re.sub("[年月到至—～\-~、\.]+", "-", s)
        s = re.sub("(?:自|日|止|\s|期间|（.*?）)", "", s)
        s = s.strip("-")
        return s
    
    def convert(self, s):
        """转换动作

        Args
        ----
        s : 待转换的字符串

        Returns
        -------
        date : 返回转换后的字符串
        """
        s_new = self.process(s)
        for p in self._patterns:
            m = p.search(s_new)
            if m:
                date = self._extract(m)
                break
        else:
            date = s
        return date
    
    @classmethod
    def of_default(cls):
        cvt = cls()
        # y-m-d
        cvt.add_pattern("^(?P<y>\d{4})[\./-](?P<m>\d{1,2})[\./-](?P<d>\d{1,2})$")
        # y-m-d至y-m-d
        cvt.add_pattern("^\d{4}[\./-]\d{1,2}[\./-]\d{1,2}[-—](?P<y>\d{4})[\./-](?P<m>\d{1,2})[\./-](?P<d>\d{1,2})$")
        # m-d
        cvt.add_pattern("^(?P<m>\d{1,2})[\./-](?P<d>\d{1,2})$")
        # m-d至m-d
        cvt.add_pattern("^\d{1,2}[\./-]\d{1,2}-(?P<m>\d{1,2})[\./-](?P<d>\d{1,2})$")
        # y-m-d至m-d
        cvt.add_pattern("^(?P<y>\d{4})[\./-]\d{1,2}[\./-]\d{1,2}[-—](?P<m>\d{1,2})[\./-](?P<d>\d{1,2})$")
        # y-m
        cvt.add_pattern("^(?P<y>\d{4})[\./-](?P<m>\d{1,2})$")
        # m-d至d
        cvt.add_pattern("^(?P<m>\d{1,2})[\./-]\d{1,2}-(?P<d>\d{1,2})$")
        # ymd
        cvt.add_pattern("^(?P<y>\d{4})(?P<m>\d{2})(?P<d>\d{2})$")
        # y-m至y-m
        cvt.add_pattern("^\d{4}[\./-]\d{1,2}-(?P<y>\d{4})[\./-](?P<m>\d{1,2})$")
        # y-m-d-d
        cvt.add_pattern("^(?P<y>\d{4})[\./-](?P<m>\d{1,2})-\d{1,2}[\./-](?P<d>\d{1,2})$")
        # ymd-ymd
        cvt.add_pattern("^\d{8}-(?P<y>\d{4})(?P<m>\d{1,2})(?P<d>\d{1,2})$")
        return cvt


class PercentConverter(BaseConverter):
    """百分比转换器

    将公告表格的百分比值转换成小数
    """
    PERCENT = Decimal('0.01')   
    def convert(self, s):
        """转换动作

        Args
        ----
        s : str
            待转换的字符串

        Returns:
        -------
        percent : str
            转换后的字符串
        """
        s_new = re.sub("[^\d\.]", "", s)
        try:
            d = Decimal(s_new)
        except decimal.InvalidOperation:
            return s
        else:
            d *= self.PERCENT
            return str(d.quantize(Decimal('0.0000')))
    

class QuantityConverter(BaseConverter):
    """数量转换器

    将公告里的股数、金额转换成数值
    """
    UNIT_10K = Decimal(10000)
    def convert(self, s):
        s_new = re.sub("[^\d\.]", "", s)
        try:
            n = Decimal(s_new)
        except decimal.InvalidOperation:
            return s
        else:
            if "万" in s:
                n *= self.UNIT_10K
            return str(n)


class PriceConverter(BaseConverter):
    """价格转换器

    将公告里的价格转换成数值
    """
    def convert(self, s):
        """转换动作

        Args
        ----
        s : 含有价格的字符串

        Returns
        -------
        price : 价格字符串
        """
        price = re.sub("[^\d\.\-]", "", s)
        #原则上要这样处理,可能是因为原手工标注不完整的原因，加上这行代码后在训练的时候效果不佳
#        price = re.split('\-', price)[-1]
        return price
    

def make_converter(cvt_name: str):
    """工厂方法
    
    统一生成各个装转换器的实例
    
    Args
    ----
    cvt_name : str
        转换器名称，目前可选的有“日期”、“百分比”、“股数”、“价格” 
        
    Ruturns
    -------
    converter : 转换器
    """
    converters = {
            "date": DateConverter.of_default,
            "percent": PercentConverter,
            "quantity": QuantityConverter,
            "price": PriceConverter,
    }
    
    names = {
            "日期": "date",
            "百分比": "percent",
            "股数": "quantity",
            "价格": "price",
            }
    name = cvt_name.lower()
    name = names.get(name, name)
    converter = converters[name]
    return converter()


if __name__ == "__main__":
    datecvt = make_converter("日期")
    percetcvt = make_converter("百分比")
    quantitycvt = make_converter("股数")
    pricecvt = make_converter("价格")

    ds = "2017-09-01-2018.6.4"
    print(ds, "==>", datecvt(ds))
    
    qs = '444,654 万股'
    print(qs, "==>", quantitycvt(qs))
    
    pers = "0.55%"
    print(pers, "==>", percetcvt(pers))
    
    prs = "60.66--66.90 元/股"
    print(prs, "==>", pricecvt(prs))
